<template>
  <div class="verify-code-container">
    <div class="verify-code">
      <input
        type="text"
        v-model="inputValue"
        @change="validateInput"
        :placeholder="placeholder"
        ref="inputRef"
      />
      <canvas
        ref="canvasRef"
        :width="width"
        :height="height"
        @click="updateVerifyCode"
      ></canvas>
      <span :class="{ success: isSuccess, error: isError }">{{
        isSuccess ? '验证码正确' : isError ? '验证码错误' : ''
      }}</span>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const props = defineProps({
  length: {
    type: Number,
    default: 5
  },
  placeholder: {
    type: String,
    default: '请输入验证码'
  },
  pool: {
    type: String,
    default: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
  },
  width: {
    type: Number,
    default: 120
  },
  height: {
    type: Number,
    default: 40
  }
})

const emit = defineEmits(['success', 'error', 'update'])

const inputValue = ref('')
const verifyCode = ref('')
const messageText = ref('请输入')
const isSuccess = ref(false)
const isError = ref(false)
const canvasRef = ref(null)
const inputRef = ref(null)

const randomNum = (min, max) => {
  return Math.floor(Math.random() * (max - min) + min)
}

const randomColor = (min, max) => {
  return `rgb(${randomNum(min, max)}, ${randomNum(min, max)}, ${randomNum(min, max)})`
}

const updateVerifyCode = () => {
  const ctx = canvasRef.value.getContext('2d')

  // 清除画布
  ctx.clearRect(0, 0, props.width, props.height)

  // 绘制背景
  ctx.fillStyle = randomColor(180, 240)
  ctx.fillRect(0, 0, props.width, props.height)

  // 生成验证码
  verifyCode.value = ''
  for (let i = 0; i < props.length; i++) {
    const letter = props.pool[randomNum(0, props.pool.length)]
    verifyCode.value += letter

    // 随机大小
    const fontSize = randomNum(18, 30)
    // 随机旋转角度
    const deg = randomNum(-20, 20)

    ctx.font = `${fontSize}px Arial`
    ctx.textBaseline = 'top'
    ctx.fillStyle = randomColor(50, 160)
    ctx.save()
    ctx.translate((props.width / props.length) * i, 0)
    ctx.rotate((deg * Math.PI) / 180)
    ctx.fillText(letter, 0, props.height / 3)
    ctx.restore()
  }

  // 绘制干扰线
  for (let i = 0; i < props.length + 2; i++) {
    ctx.beginPath()
    ctx.moveTo(randomNum(0, props.width), randomNum(0, props.height))
    ctx.lineTo(randomNum(0, props.width), randomNum(0, props.height))
    ctx.strokeStyle = randomColor(50, 160)
    ctx.closePath()
    ctx.stroke()
  }

  // 重置状态
  resetValidationState()
}

const validateInput = () => {
  if (inputValue.value.toUpperCase() === verifyCode.value.toUpperCase()) {
    messageText.value = '验证码正确'
    isSuccess.value = true
    isError.value = false
    emit('success', verifyCode.value)
  } else {
    messageText.value = '验证码错误'
    isSuccess.value = false
    isError.value = true
    emit('error', inputValue.value)
  }
}

const resetValidationState = () => {
  messageText.value = '请输入'
  isSuccess.value = false
  isError.value = false
  inputValue.value = ''
  emit('update')
}

const focusInput = () => {
  inputRef.value.focus()
}

// 暴露方法给父组件
defineExpose({
  updateVerifyCode,
  focusInput,
  validateInput
})

onMounted(() => {
  updateVerifyCode()
})
</script>

<style lang="scss" scoped>
.verify-code-container {
  width: 100%;
  display: flex;
  gap: 20px;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.verify-code {
  display: flex;
  align-items: center;
  span {
    &.success {
      color: green;
    }

    &.error {
      color: red;
    }
  }
}
input {
  margin-right: 10px;
  height: 40px;
  padding: 0 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

canvas {
  cursor: pointer;
  border: 1px solid #eee;
  border-radius: 4px;
}
</style>
